package com.zws.cucumber.spring;

import com.zws.cucumber.util.colorfullog.ZwsColorfulLogHelper;
import com.zws.cucumber.util.colorfullog.ZwsColorfulLogger;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.metrics.StartupStep;

import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;

public class ZwsCucumberStartupStep implements StartupStep {

    private static final Logger logger = ZwsColorfulLogger.of(LoggerFactory.getLogger(ZwsCucumberStartupStep.class));

    private static Stack<ZwsCucumberStartupStep> stepStack = new Stack<>();

    private AtomicInteger counter = new AtomicInteger(0);

    private long startTime = System.nanoTime();
    private Long elapsed;
    private Long ms, us, ns;

    private long id = counter.incrementAndGet();
    private String name;
    private Long parentId;
    private List<Tag> tagList = new ArrayList<>();
    private List<ZwsCucumberStartupStep> children = new ArrayList<>();

    public ZwsCucumberStartupStep(String name) {
        this.name = name;

        if (!stepStack.isEmpty()) {
            this.parentId = stepStack.peek().getId();
            stepStack.peek().children.add(this);
        }
        stepStack.push(this);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public long getId() {
        return id;
    }

    @Override
    public Long getParentId() {
        return parentId;
    }

    @Override
    public StartupStep tag(String key, String value) {
        ZwsCucumberTag tag = new ZwsCucumberTag(key, value);
        tagList.add(tag);
        return this;
    }

    @Override
    public StartupStep tag(String key, Supplier<String> value) {
        ZwsCucumberTag tag = new ZwsCucumberTag(key, value.get());
        tagList.add(tag);
        return this;
    }

    @Override
    public Tags getTags() {
        return () -> tagList.iterator();
    }

    @Override
    public void end() {
        elapsed = System.nanoTime() - startTime;
        ms = elapsed / 1000000L;
        us = (elapsed % 1000000L) / 1000L;
        ns = (elapsed % 1000000L) % 1000L;
        stepStack.pop();
        if (parentId != null) {
            return;
        }

        logger.info("Elapsed Of ApplicationContext");
        if (ms > 500) {
            logger.info("\n<r>{}ms {} - {}</r>\n{}", "%3d".formatted(ms), getName(), tagList, childrenString());
        } else if (ms > 50) {
            logger.info("\n<y>{}ms {} - {}</y>\n{}", "%3d".formatted(ms), getName(), tagList, childrenString());
        } else {
            logger.info("\n{}ms {} - {}\n{}", "%3d".formatted(ms), getName(), tagList, childrenString());
        }

    }

    private String childrenString() {
        return childrenString(new StringBuilder());
    }

    private String childrenString(StringBuilder prefix) {
        if (children.isEmpty()) {
            return "";
        }
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < children.size(); i++) {
            ZwsCucumberStartupStep child = children.get(i);
            sb.append(prefix);
            if (SystemUtils.IS_OS_WINDOWS) {
                if (child.ms != null) {
                    if (child.ms > 500) {
                        sb.append(ZwsColorfulLogHelper.escape(ZwsColorfulLogHelper.FG_RED));
                    } else if (child.ms > 50) {
                        sb.append(ZwsColorfulLogHelper.escape(ZwsColorfulLogHelper.FG_YELLOW));
                    }
                }
            }
            sb.append("\t+--")
                    .append(child.ms != null ? "%3dms".formatted(child.ms) : "no ms")
                    .append(" ")
                    .append(child.getName())
                    .append(" - ")
                    .append(child.tagList)
                    .append("\n");
            if (SystemUtils.IS_OS_WINDOWS) {
                if (child.ms != null) {
                    if (child.ms > 50) {
                        sb.append(ZwsColorfulLogHelper.ANSI_RESET);
                    }
                }
            }
            if (i == children.size() - 1) {
                prefix.append("\t ");
            } else {
                prefix.append("\t|");
            }
            sb.append(child.childrenString(prefix));
            prefix.delete(prefix.length() - 2, prefix.length());
        }
        return sb.toString();
    }

    public Long getElapsed() {
        return elapsed;
    }
}

class ZwsCucumberTag implements StartupStep.Tag {

    private final String key;

    private final String value;

    public ZwsCucumberTag(String key, String value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public String getKey() {
        return this.key;
    }

    @Override
    public String getValue() {
        return this.value;
    }

    @Override
    public String toString() {
        return key + ":" + value;
    }
}